#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/socket.h>
#include <unistd.h>
#include <netinet/in.h>

#define PORT 9999
#define BUF_SIZE 4096

// 关闭 socket
int close_socket(int sock) {
    return close(sock) == 0 ? 0 : 1;
}

// 检查请求是否合法
int is_valid_request(const char* request) {
    // 检查请求是否太短
    if (strlen(request) < 14) return 0; // 最短合法请求如"GET / HTTP/1.0\r\n"

    // 找到第一个\r\n的位置（请求行结束）
    const char* first_line_end = strstr(request, "\r\n");
    if (first_line_end == NULL) return 0; // 没有行结束符，无效

    // 检查请求行
    char first_line[BUF_SIZE];
    size_t first_line_len = first_line_end - request;
    if (first_line_len >= BUF_SIZE) return 0; // 第一行太长
    strncpy(first_line, request, first_line_len);
    first_line[first_line_len] = '\0';

    // 检查是否有前导空格
    if (first_line[0] == ' ' || first_line[0] == '\t') return 0; // 请求行不能以空格或制表符开头

    // 检查请求行是否完整包含方法、路径和HTTP版本
    char method[16], path[BUF_SIZE], version[16];
    int fields = sscanf(first_line, "%15s %1023s %15s", method, path, version);
    if (fields != 3) return 0; // 必须有三个部分：方法、路径、版本

    if (strncmp(version, "HTTP/", 5) != 0) return 0; // 必须是HTTP版本

    // 检查头部格式
    const char* header_start = first_line_end + 2; // 跳过第一个\r\n
    const char* request_end = strstr(request, "\r\n\r\n");
    if (request_end == NULL) return 0; // 没有结束标志，无效

    char headers[BUF_SIZE];
    size_t headers_len = request_end - header_start;
    if (headers_len >= BUF_SIZE) return 0;
    strncpy(headers, header_start, headers_len);
    headers[headers_len] = '\0';

    // 如果有头部，检查每一行的格式
    if (headers_len > 0) {
        char* line = headers;
        while (line && *line) {
            char* next_line = strstr(line, "\r\n");
            if (next_line) {
                *next_line = '\0'; // 临时终止当前行
                next_line += 2;    // 跳到下一行
            }

            // 检查是否是合法的头部格式 "字段名: 字段值"
            char* colon = strchr(line, ':');
            if (colon == NULL || colon == line || *(colon + 1) != ' ') {
                return 0; // 没有冒号，或冒号在开头，或冒号后没有空格，无效
            }

            line = next_line;
        }
    }

    // 检查支持的方法
    if (strcmp(method, "GET") == 0 || strcmp(method, "HEAD") == 0 || strcmp(method, "POST") == 0) {
        return 1; // 有效且支持的方法
    }

    return 2; // 有效但不支持的方法
}

// 处理请求
void handle_request(int client_sock, char* buffer, ssize_t len) {
    buffer[len] = '\0';
    int validity = is_valid_request(buffer);
    
    if (validity == 0) {
        const char* bad_request = "HTTP/1.1 400 Bad Request\r\n\r\n";
        send(client_sock, bad_request, strlen(bad_request), 0);
        printf("Sent 400 Bad Request\n");
    } else if (validity == 1) {
        send(client_sock, buffer, len, 0);
        printf("Echoed request\n");
    } else { // validity == 2
        const char* not_implemented = "HTTP/1.1 501 Not Implemented\r\n\r\n";
        send(client_sock, not_implemented, strlen(not_implemented), 0);
        printf("Sent 501 Not Implemented\n");
    }
}

// 主函数
int main() {
    int sock, client_sock;
    struct sockaddr_in addr, cli_addr;
    socklen_t cli_size;
    char buffer[BUF_SIZE];

    printf("----- HTTP Echo Server -----\n");

    if ((sock = socket(AF_INET, SOCK_STREAM, 0)) == -1) return EXIT_FAILURE;

    int opt = 1;
    setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt));

    addr.sin_family = AF_INET;
    addr.sin_port = htons(PORT);
    addr.sin_addr.s_addr = INADDR_ANY;

    if (bind(sock, (struct sockaddr*)&addr, sizeof(addr))) return EXIT_FAILURE;
    if (listen(sock, 5)) return EXIT_FAILURE;

    while (1) {
        cli_size = sizeof(cli_addr);
        client_sock = accept(sock, (struct sockaddr*)&cli_addr, &cli_size);
        if (client_sock == -1) continue;

        ssize_t total_received = 0;
        do {
            ssize_t readret = recv(client_sock, buffer + total_received, 
                                 BUF_SIZE - total_received - 1, 0);
            if (readret <= 0) break;
            total_received += readret;
            buffer[total_received] = '\0';
            if (strstr(buffer, "\r\n\r\n") != NULL) break;
        } while (total_received < BUF_SIZE - 1);

        if (total_received > 0) {
            printf("Received request (%zd bytes):\n%s\n", total_received, buffer);
            handle_request(client_sock, buffer, total_received);
        } else {
            printf("No data received\n");
        }

        close_socket(client_sock);
    }

    close_socket(sock);
    return EXIT_SUCCESS;
}